﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.UI;
using System.Web.UI.WebControls.WebParts;
using Microsoft.SharePoint;
using SharePoint.Sunbox.Sandbox.Core;
using SharePoint.Sunbox.Sandbox.Resources;

namespace SharePoint.Sunbox.Sandbox.CommentsWebPart
{
    [ToolboxItemAttribute(false)]
    public class CommentsWebPart : WebPart, IWebEditable
    {
        public string OptionsObject { get; set; }

        [Personalizable(PersonalizationScope.Shared), WebBrowsable(true),
         LocalizedWebDisplayNameAttribute("CommentsWebPartListPropertyTitle"),
         LocalizedWebDescription("CommentsWebPartListPropertyDescription")]
        public string CommentsListName { get; set; }

        [Personalizable(PersonalizationScope.Shared), WebBrowsable(true),
         LocalizedWebDisplayNameAttribute("CommentsWebPartCommentTextFieldPropertyTitle"),
         LocalizedWebDescription("CommentsWebPartCommentTextFieldPropertyDescription")]
        public string CommentsTextFieldName { get; set; }

        [Personalizable(PersonalizationScope.Shared), WebBrowsable(true),
         LocalizedWebDisplayNameAttribute("CommentsWebPartPartentListItemFieldPropertyTitle"),
         LocalizedWebDescription("CommentsWebPartPartentListItemFieldPropertyDescription")]
        public string CommentsArticleIdFieldName { get; set; }

        [Personalizable(PersonalizationScope.Shared), WebBrowsable(true),
 LocalizedWebDisplayNameAttribute("CommentsWebPartJsObjectNameTitle"),
 LocalizedWebDescription("CommentsWebPartJsObjectNameDescription")]
        public string JsObjectName { get; set; }

        [Personalizable(PersonalizationScope.Shared), WebBrowsable(true),
         LocalizedWebDisplayNameAttribute("CommentsWebPartPartentCommentFieldPropertyTitle"),
         LocalizedWebDescription("CommentsWebPartPartentCommentFieldPropertyDescription")]
        public string CommentsParentIdFieldName { get; set; }

        [Personalizable(PersonalizationScope.Shared), WebBrowsable(true),
         LocalizedWebDisplayNameAttribute("CommentsWebPartTargetListPropertyTitle"),
         DefaultValue(""),
         LocalizedWebDescription("CommentsWebPartTargetListPropertyDescription")]
        public string ArticleListName { get; set; }

        //[Personalizable(PersonalizationScope.Shared), WebBrowsable(true),
        // WebDisplayName("Comments count field"),
        // WebDescription("")]
        //public string ArticleCommentsCountFieldName { get; set; }

        [Personalizable(PersonalizationScope.Shared), WebBrowsable(true),
         LocalizedWebDisplayNameAttribute("CommentWebPartMinCommentsPropertyTitle"),
         DefaultValue(0),
         LocalizedWebDescription("CommentWebPartMinCommentsPropertyDescription")]
        public int MinCommentsToShowBottomForm { get; set; }

        [Personalizable(PersonalizationScope.Shared), WebBrowsable(true),
         LocalizedWebDisplayNameAttribute("CommentWebPartQueryParameterPropertyTitle"),
         LocalizedWebDescription("CommentWebPartQueryParameterPropertyDescription")]
        public string QueryParameterName { get; set; }

        [Personalizable(PersonalizationScope.Shared), WebBrowsable(true),
         LocalizedWebDisplayNameAttribute("CommentsWebPartAlterCssPropertyTitle"),
         DefaultValue(""),
         LocalizedWebDescription("CommentsWebPartAlterCssPropertyTitle")]
        public string AlternateCssFilePath { get; set; }

        EditorPartCollection IWebEditable.CreateEditorParts()
        {
            // control the editorparts
            var editors = new List<EditorPart>();
            var dataSourcesEditorPart = new CommentsEditorPart
            {
                ID = ID + "_settingseditorPart",
                Title = Properties.CommentsWebPartEmptyListMessage
            };
            editors.Add(dataSourcesEditorPart);
            return new EditorPartCollection(editors);
        }

        protected override void Render(HtmlTextWriter writer)
        {
            base.Render(writer);
            try
            {
                LoadSettings();
            }
            catch (Exception ex)
            {
                writer.Write(string.Format("{0}", ex.Message));
                return;
            }

            if (!string.IsNullOrEmpty(QueryParameterName) &&
                string.IsNullOrEmpty(Convert.ToString(Page.Request.QueryString[QueryParameterName])))
            {
                return;
            }

            string siteUrl = SPContext.Current.Site.RootWeb.Url;
            //CSS:
            writer.Write(string.Format("<link rel=\"stylesheet\" type=\"text/css\" href=\"{0}/Style%20Library/Sunbox/Comments/comments.css\" />", siteUrl));
            if (!string.IsNullOrEmpty(AlternateCssFilePath))
                writer.Write(string.Format("<link rel=\"stylesheet\" type=\"text/css\" href=\"{0}\" />", AlternateCssFilePath));
            //JS:
            writer.Write(string.Format(@"<script type=""text/javascript"">
                                        if (typeof(jQuery) == ""undefined"") document.write(""<script src='{0}/Style Library/Sunbox/Scripts/jquery.js'></"" + ""script>""); 
                                        if (typeof(SharePointOrmIncluded) == ""undefined"") document.write(""<script src='{0}/Style Library/Sunbox/Scripts/sharepoint.orm.js'></"" + ""script>"");
                                        </script>
                                        <script type=""text/javascript"" src=""{0}/Style Library/Sunbox/Scripts/ckeditor/ckeditor.js""></script>
                                        <script type=""text/javascript"" src=""{0}/Style Library/Sunbox/Comments/Scripts/sunbox.comments.js""></script>
                                        <script type=""text/javascript"" src=""{0}/Style Library/Sunbox/Comments/Scripts/lang/{1}.js""></script>", siteUrl, Properties.Language));
            string containerId = "comments-outer-" + Guid.NewGuid().ToString();
            writer.Write(string.Format("<div class=\"sunbox-comments-outer\" id=\"{0}\"></div>", containerId));
            //apply script:
            string parameterName = string.IsNullOrEmpty(JsObjectName) ? string.Empty : string.Format("var {0} = ", JsObjectName);
            writer.Write("<script type=\"text/javascript\">{0} {2} $(\"#{1}\").commentslist(options);</script>", OptionsObject, containerId, parameterName);
        }



        private void LoadSettings()
        {
            var parameters = new List<JsParameter>();
            if (SPContext.Current == null)
                throw new Exception("Missing SharePoint context");
            if (string.IsNullOrEmpty(CommentsListName))
                throw new Exception(Properties.CommentsWebPartEmptyListMessage);

            parameters.Add(new JsParameter { ParameterName = "commentsListName", ParameterValue = CommentsListName, IsString = true });

            SPWeb web = SPContext.Current.Web;
            SPList commentsList;
            if (IsGuid(CommentsListName))
            {
                commentsList = web.Lists[new Guid(CommentsListName)];
                if (commentsList == null)
                    throw new Exception(string.Format(Properties.CommentsWebPartListIdNotFoundMessage, CommentsListName));
            }
            else
            {
                commentsList = web.Lists[CommentsListName];
                if (commentsList == null)
                    throw new Exception(string.Format(Properties.CommentsWebPartListNotFoundMessage, CommentsListName));
            }
            if (commentsList.DoesUserHavePermissions(SPBasePermissions.DeleteListItems))
                parameters.Add(new JsParameter { ParameterName = "enableDelete", ParameterValue = "true", IsString = false });
            if (string.IsNullOrEmpty(QueryParameterName))
                throw new Exception(Properties.CommentsWebPartEmptyQueryParameterMessage);

            if (!string.IsNullOrEmpty(CommentsTextFieldName))
            {
                ValidateField(commentsList, CommentsTextFieldName);
                parameters.Add(new JsParameter
                {
                    ParameterName = "commentsTextFieldName",
                    ParameterValue = CommentsTextFieldName,
                    IsString = true
                });
            }

            if (!string.IsNullOrEmpty(CommentsArticleIdFieldName))
            {
                SPField articleField = ValidateField(commentsList, CommentsArticleIdFieldName);
                parameters.Add(new JsParameter
                {
                    ParameterName = "commentsArticleIdFieldName",
                    ParameterValue = CommentsArticleIdFieldName,
                    IsString = true
                });

                if (articleField is SPFieldLookup)
                    parameters.Add(new JsParameter
                    {
                        ParameterName = "commentsArticleIdFieldType",
                        ParameterValue = "Lookup",
                        IsString = true
                    });
                else if (articleField is SPFieldText)
                    parameters.Add(new JsParameter
                    {
                        ParameterName = "commentsArticleIdFieldType",
                        ParameterValue = "Text",
                        IsString = true
                    });
            }
            if (!string.IsNullOrEmpty(CommentsParentIdFieldName))
            {
                ValidateField(commentsList, CommentsParentIdFieldName);
                parameters.Add(new JsParameter
                {
                    ParameterName = "commentsParentFieldName",
                    ParameterValue = CommentsParentIdFieldName,
                    IsString = true
                });
            }

            /* ====================================== ARTICLES ======================== */
            if (!string.IsNullOrEmpty(ArticleListName))
            {
                SPList articleList;
                if (IsGuid(ArticleListName))
                {
                    articleList = web.Lists[new Guid(ArticleListName)];
                    if (articleList == null)
                        throw new Exception(string.Format(Properties.CommentsWebPartParentListIdNotFoundMessage, ArticleListName));
                }
                else
                {
                    articleList = web.Lists[ArticleListName];
                    if (articleList == null)
                        throw new Exception(string.Format(Properties.CommentsWebPartParentListNotFoundMessage, ArticleListName));
                }
                parameters.Add(new JsParameter
                {
                    ParameterName = "articleListName",
                    ParameterValue = ArticleListName,
                    IsString = true
                });
                /*if (string.IsNullOrEmpty(ArticleCommentsCountFieldName))
                    throw new Exception(string.Format("Comment count field not specified {0}",
                                                      articleList.Title));

                ValidateField(articleList, ArticleCommentsCountFieldName);
                parameters.Add(new JsParameter
                {
                    ParameterName = "articleCommentsCountFieldName",
                    ParameterValue = ArticleCommentsCountFieldName,
                    IsString = true
                });*/
            }

            if (!string.IsNullOrEmpty(QueryParameterName))
            {
                parameters.Add(new JsParameter
                {
                    ParameterName = "queryParameterName",
                    ParameterValue = QueryParameterName,
                    IsString = true
                });
            }

            if (MinCommentsToShowBottomForm > 0)
            {
                parameters.Add(new JsParameter
                {
                    ParameterName = "minCommentsToShowBottomForm",
                    ParameterValue =
                        MinCommentsToShowBottomForm.ToString(CultureInfo.InvariantCulture),
                    IsString = false
                });
            }
            parameters.Add(new JsParameter { IsString = true, ParameterName = "lang", ParameterValue = Properties.Language });
            OptionsObject = BuildJsOptionsObject(parameters);
        }

        private SPField ValidateField(SPList list, string fieldName)
        {
            bool field = list.Fields.ContainsFieldWithStaticName(fieldName);
            if (!field)
                throw new Exception(string.Format(Properties.CommentsWebPartFieldNotFoundMessage,
                                                  fieldName, list.Title));
            return list.Fields.GetFieldByInternalName(fieldName);
        }

        private static string BuildJsOptionsObject(List<JsParameter> parameters)
        {
            var sb = new StringBuilder();
            for (int i = 0; i < parameters.Count; i++)
            {
                sb.Append(string.Format("{0}:{1},",
                                        parameters[i].ParameterName,
                                        (parameters[i].IsString
                                             ? "\"" + parameters[i].ParameterValue + "\""
                                             : parameters[i].ParameterValue)));
                if (i == parameters.Count - 1)
                {
                    sb.Remove(sb.Length - 1, 1);
                }
            }

            return string.Format("var options = {{{0}}};", sb);
        }

        private static bool IsGuid(string stringValue)
        {
            const string guidPattern = @"[a-fA-F0-9]{8}(-[a-fA-F0-9]{4}){3}-[a-fA-F0-9]{12}";
            if (string.IsNullOrEmpty(stringValue))
                return false;
            var regex = new Regex(guidPattern);
            return regex.IsMatch(stringValue);
        }

        #region Nested type: JsParameter

        private class JsParameter
        {
            public string ParameterName { get; set; }
            public string ParameterValue { get; set; }
            public bool IsString { get; set; }
        }

        #endregion
    }
}
